LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;

ENTITY GameControl IS
	PORT(
		Clk : IN std_logic;
		ClkDispEle : IN std_logic;
		Start : IN std_logic;
		Finish : OUT std_logic;
		Reset : IN std_logic;
		Pause : OUT std_logic;
		
		GameMode : IN std_logic_vector(1 downto 0);
		GameMap : IN std_logic_vector(1 downto 0);
		GameResult : OUT std_logic_vector(1 DOWNTO 0);

		PrimAddr    : OUT std_logic_vector(9 downto 0);
		PrimReadData    : IN  std_logic_vector(15 downto 0);
		PrimWriteData	: OUT std_logic_vector(15 downto 0);
		PrimWriteEnable	: OUT std_logic;
		SecoAddr   : OUT std_logic_vector(9 downto 0);
		SecoWriteData   : OUT std_logic_vector(15 downto 0);
		SecoWriteEnable : OUT std_logic;
		SecoReadData    : IN  std_logic_vector(15 downto 0);
		
		RawP1Key : IN std_logic_vector(5 downto 0);	
		RawP2Key : IN std_logic_vector(5 downto 0);
		
		oP1HP, oP2HP : OUT std_logic_vector(1 DOWNTO 0);
		
		GameResultInput : IN std_logic_vector(1 DOWNTO 0);
		
		RandomIn : IN std_logic_vector(15 DOWNTO 0);
		RandomIn2 : IN std_logic_vector(15 downto 0);
		
		stateout : OUT std_logic_vector(2 downto 0);
		DirOut : out std_logic_vector(2 downto 0)
	);
END GameControl;

ARCHITECTURE rtl OF GameControl IS
	COMPONENT DataLoad IS
		PORT(
			Clk : IN std_logic;
			Start : IN std_logic;
			Finish : OUT std_logic;
			Reset : IN std_logic;
			SUp, SDown, SLeft, SRight, SCenter : OUT std_logic_vector(15 downto 0);
			PosX, PosY : IN std_logic_vector(4 downto 0);

			PrimReadAddr    : OUT std_logic_vector(9 downto 0);
			PrimReadData    : IN  std_logic_vector(15 downto 0)
		);
	END COMPONENT;
	
	COMPONENT MapBase IS
		PORT
		(
			address		: IN STD_LOGIC_VECTOR (11 DOWNTO 0);
			clock		: IN STD_LOGIC ;
			q		: OUT STD_LOGIC_VECTOR (3 DOWNTO 0)
		);
	END COMPONENT;
	
	COMPONENT CalcCell IS
		PORT(
			Clk : IN std_logic;
			Start : IN std_logic;
			Finish : OUT std_logic;
			Reset : IN std_logic;
			SUp, SDown, SLeft, SRight, SCenter : IN std_logic_vector(15 downto 0);
			Result : OUT std_logic_vector(1 DOWNTO 0);
			NCenter : OUT std_logic_vector(15 downto 0);
			PosX, PosY : IN std_logic_vector(4 downto 0);
			P1KeyDir : IN std_logic_vector(2 downto 0);
			P1KeyFire : IN std_logic;
			P1KeyExit : IN std_logic;	
			P2KeyDir : IN std_logic_vector(2 downto 0);
			P2KeyFire : IN std_logic;
			P2KeyExit : IN std_logic;
			----
			-- HP Output
			HP1, HP2 : OUT std_logic_vector(1 DOWNTO 0);
			-- Random number
			RandomIn : IN std_logic_vector(15 DOWNTO 0);
			RandomIn2: IN std_logic_vector(15 downto 0);
			-- Counter in
			gCounter : IN std_logic_vector(20 DOWNTO 0);
			gCounterSlow : IN std_logic_vector(10 DOWNTO 0)
		);
	END COMPONENT;


	TYPE State_Type IS (Idle,
						ClearMap, ClearMap_Pre, ClearMap_Do, ClearMap_Wait,
						LoadMap, LoadMap_Read, LoadMap_Read_Wait, LoadMap_Write, LoadMap_Write_Do,
						LoadMap_Wait,
						GameLooping,
						MapLooping,
						UpdateMap_Load,
						UpdateMap_Calculate,
						UpdateMap_Store_Pre, UpdateMap_Store,
						StoreMap, StoreMap_Read, StoreMap_Read_Wait, StoreMap_Write,
						StoreMap_Write_Pre, StoreMap_Wait,
						Pausing,
						GameOver
						);
	
	SIGNAL state, bakstate : State_Type;
	SIGNAL sCounter : std_logic_vector(31 downto 0);
	
	--SIGNAL Paused : std_logic;
	SIGNAL SUp, SDown, SLeft, SRight, SCenter : std_logic_vector(15 downto 0);
	SIGNAL NCenter : std_logic_vector(15 downto 0);
	SIGNAL PosX, PosY : std_logic_vector(4 downto 0);
	SIGNAL DataLoadStart, DataLoadFinish : std_logic;
	SIGNAL CalcCellStart, CalcCellFinish : std_logic;
	SIGNAL MapRomAddr : std_logic_vector(11 downto 0);
	SIGNAL MapRomData : std_logic_vector(3 downto 0);
	
	TYPE KeyCounter IS ARRAY(5 DOWNTO 0) OF std_logic_vector(2 DOWNTO 0);
	SIGNAL P1KeyCounter, P2KeyCounter : KeyCounter;
	SIGNAL P1KeyDir : std_logic_vector(2 downto 0);
	SIGNAL P1KeyFire : std_logic;
	SIGNAL P1KeyExit : std_logic;	
	SIGNAL P2KeyDir : std_logic_vector(2 downto 0);
	SIGNAL P2KeyFire : std_logic;
	SIGNAL P2KeyExit : std_logic;
	
	SIGNAL GameRes : std_logic_vector(1 DOWNTO 0);
	
	SIGNAL P1HP, P2HP : std_logic_vector(1 DOWNTO 0);
	
	SIGNAL PrimReadAddr, PrimWriteAddr: std_logic_vector(9 DOWNTO 0);
	SIGNAL PrimWriteSelector : std_logic;
	
	SIGNAL GameCounter : std_logic_vector(20 DOWNTO 0);
	SIGNAL GameCounterSlow : std_logic_vector(10 DOWNTO 0);
BEGIN

--	stateout  <= "000" WHEN state = Idle ELSE
--				 "001" WHEN state = LoadMap ELSE
--				 "010" WHEN state = GameLooping ELSE
--				 "011" WHEN state = MapLooping ELSE
--				 "100" WHEN state = UpdateMap_Load ELSE
--				 "101" WHEN state = UpdateMap_Calculate ELSE
--				 "110" WHEN state = UpdateMap_Store ELSE
--				 "111" WHEN state = StoreMap ELSE
--				 "010";

	--DirOut <= stateout;
--	stateout(0) <= P1KeyExit;
--	stateout(1) <= '1' WHEN state = Pausing ELSE '0';

	oP1HP <= P1HP;
	oP2HP <= P2HP;
	
	GameResult <= GameRes;
	
	cDataLoad : DataLoad PORT MAP(
			Clk => Clk,
			Start => DataLoadStart,
			Finish => DataLoadFinish,
			Reset => Reset,
			SUp => SUp,
			SDown => SDown,
			SLeft => SLeft,
			SRight => SRight,
			SCenter => SCenter,
			PosX => PosX,
			PosY => PosY,
			PrimReadAddr => PrimReadAddr,
			PrimReadData => PrimReadData );
			
	cMapRom : MapBase PORT MAP(
			address => MapRomAddr,
			clock => ClkDispEle,
			q => MapRomData );
			
	cCalcCell : CalcCell PORT MAP(
			Clk => Clk,
			Start => CalcCellStart,
			Finish => CalcCellFinish,
			Reset => Reset,
			SUp => SUp,
			SDown => SDown,
			SLeft => SLeft,
			SRight => SRight,
			SCenter => SCenter,
			PosX => PosX,
			PosY => PosY,
			NCenter => NCenter,
			Result => GameRes,
			P1KeyDir  => P1KeyDir,
			P1KeyFire => P1KeyFire,
			P1KeyExit => P1KeyExit,
			P2KeyDir  => P2KeyDir,
			P2KeyFire => P2KeyFire,
			P2KeyExit => P2KeyExit,
			----
			-- HP Output
			HP1 => P1HP,
			HP2 => P2HP,
			-- Random number
			RandomIn => RandomIn,
			RandomIn2 => RandomIn2,
			-- Counter in
			gCounter => GameCounter,
			gCounterSlow => GameCounterSlow );
		
	-- encoding keys priority encoder....
	P1KeyExit <= '1' WHEN P1KeyCounter(5) = 1 ELSE '0';
	P1KeyFire <= '1' WHEN P1KeyCounter(4) = 1 ELSE '0';
	P1KeyDir  <= "000" WHEN P1KeyCounter(3) = 1 ELSE
				 "001" WHEN P1KeyCounter(2) = 1 ELSE
				 "010" WHEN P1KeyCounter(1) = 1 ELSE
				 "011" WHEN P1KeyCounter(0) = 1 ELSE
				 "100";
	
	P2KeyExit <= '1' WHEN P2KeyCounter(5) = 1 ELSE '0';
	P2KeyFire <= '1' WHEN P2KeyCounter(4) = 1 ELSE '0';
	P2KeyDir <= "000" WHEN P2KeyCounter(3) = 1 ELSE
				 "001" WHEN P2KeyCounter(2) = 1 ELSE
				 "010" WHEN P2KeyCounter(1) = 1 ELSE
				 "011" WHEN P2KeyCounter(0) = 1 ELSE
				 "100";
				 
	PrimAddr <= PrimReadAddr WHEN PrimWriteSelector = '0' ELSE
				PrimWriteAddr;
				 
	cMainCon : PROCESS(Clk, Reset)
	BEGIN
		IF Reset = '0' THEN
			state <= idle;
		ELSIF rising_edge(Clk) THEN
			CASE state IS
				WHEN Idle =>
					Finish <= '0';
					IF Start = '1' THEN
						state <= ClearMap;
						PosX <= "00000";
						PosY <= "00000";
						--  Initial Hp
						--P1HP <= "11";
						--P2HP <= "11";
						
						GameCounter <= (OTHERS => '0');
						GameCounterSlow <= (OTHERS => '0');
						P1KeyCounter <= (OTHERS => (OTHERS => '0'));	-- clear key state
						P2KeyCounter <= (OTHERS => (OTHERS => '0'));
					ELSE
						state <= Idle;
					END IF;
					
				WHEN ClearMap =>
					-- map rom data to primmemory data
					PrimWriteEnable <= '0';
					PrimWriteSelector <= '1';
					PrimWriteData <= "0000000000001001";
					IF PosX = 26 THEN
						IF PosY = 19 THEN
							state <= LoadMap;
							PosX <= "00000";
							PosY <= "00001";
						ELSE
							PosX <= "00000";
							PosY <= PosY +1;
							state <= ClearMap_Pre;
						END IF;
					ELSE
						PosX <= PosX +1;
						state <= ClearMap_Pre;
					END IF;
				WHEN ClearMap_Pre =>
					state <= ClearMap_Do;
					PrimWriteAddr <= PosY & PosX;
					
				WHEN ClearMap_Do =>
					PrimWriteEnable <= '1';
					state <= ClearMap_Wait;
					sCounter <= (OTHERS => '0');
				WHEN ClearMap_Wait =>
					IF sCounter = 5 THEN
						state <= ClearMap;
						PrimWriteEnable <= '0';
					ELSE
						sCounter <= sCounter +1;
						state <= ClearMap_Wait;
					END IF;
					
					
			
				WHEN LoadMap =>
					-- map rom data to primmemory data
					-----------------------------owner---------------tank hp----------------disp----------
					PrimWriteEnable <= '0';
					PrimWriteSelector <= '1';
					IF PosX = 25 THEN
						IF PosY = 18 THEN
							state <= GameLooping;
							PosX <= "00000";
							PosY <= "00001";
						ELSE
							PosX <= "00001";
							PosY <= PosY +1;
							state <= LoadMap_Read;
						END IF;
					ELSE
						PosX <= PosX +1;
						state <= LoadMap_Read;
					END IF;
				WHEN LoadMap_Read =>
					state <= LoadMap_Read_Wait;
					sCounter <= (OTHERS => '0');
					MapRomAddr <= GameMap & PosY & PosX;
					PrimWriteAddr <= PosY & PosX;
				WHEN LoadMap_Read_Wait =>
					IF sCounter = 1 THEN
						state <= LoadMap_Write;
					ELSE
						sCounter <= sCounter +1;
						state <= LoadMap_Read_Wait;
					END IF;					
				WHEN LoadMap_Write =>
					-----------------------------owner---------------tank hp----------------disp----------				
					PrimWriteData <= "000110000000" & MapRomData(3 DOWNTO 0);
					state <= LoadMap_Write_Do;
				WHEN LoadMap_Write_Do =>
					PrimWriteEnable <= '1';
					state <= LoadMap_Wait;
					sCounter <= (OTHERS => '0');
				WHEN LoadMap_Wait =>
					IF sCounter = 2 THEN
						state <= LoadMap;
						PrimWriteEnable <= '0';
					ELSE
						sCounter <= sCounter +1;
						state <= LoadMap_Wait;
					END IF;

				WHEN GameLooping =>
					IF GameMode = "11" THEN
						state <= StoreMap;
						PosX <= "00000";
						PosY <= "00001";
						
					ELSE
						-- add keyboard count
						--FOR i IN 0 TO 5 GENERATE
						---------------------------------------------------------------------
							IF RawP1Key(0) = '1' THEN
								P1KeyCounter(0) <= P1KeyCounter(0) +1;
							ELSE
								P1KeyCounter(0) <= (OTHERS => '0');
							END IF;
							IF RawP1Key(1) = '1' THEN
								P1KeyCounter(1) <= P1KeyCounter(1) +1;
							ELSE
								P1KeyCounter(1) <= (OTHERS => '0');
							END IF;
							IF RawP1Key(2) = '1' THEN
								P1KeyCounter(2) <= P1KeyCounter(2) +1;
							ELSE
								P1KeyCounter(2) <= (OTHERS => '0');
							END IF;
							IF RawP1Key(3) = '1' THEN
								P1KeyCounter(3) <= P1KeyCounter(3) +1;
							ELSE
								P1KeyCounter(3) <= (OTHERS => '0');
							END IF;
							IF RawP1Key(4) = '1' THEN
								P1KeyCounter(4) <= P1KeyCounter(4) +1;
							ELSE
								P1KeyCounter(4) <= (OTHERS => '0');
							END IF;
							IF RawP1Key(5) = '1' THEN
								P1KeyCounter(5) <= P1KeyCounter(5) +1;
							ELSE
								P1KeyCounter(5) <= (OTHERS => '0');
							END IF;
						---------------------------------------------------------------------
						--END GENERATE;
						--FOR i IN 0 TO 5 GENERATE
						---------------------------------------------------------------------
							IF RawP2Key(0) = '1' THEN
								P2KeyCounter(0) <= P2KeyCounter(0) +1;
							ELSE
								P2KeyCounter(0) <= (OTHERS => '0');
							END IF;
							IF RawP2Key(1) = '1' THEN
								P2KeyCounter(1) <= P2KeyCounter(1) +1;
							ELSE
								P2KeyCounter(1) <= (OTHERS => '0');
							END IF;
							IF RawP2Key(2) = '1' THEN
								P2KeyCounter(2) <= P2KeyCounter(2) +1;
							ELSE
								P2KeyCounter(2) <= (OTHERS => '0');
							END IF;
							IF RawP2Key(3) = '1' THEN
								P2KeyCounter(3) <= P2KeyCounter(3) +1;
							ELSE
								P2KeyCounter(3) <= (OTHERS => '0');
							END IF;
							IF RawP2Key(4) = '1' THEN
								P2KeyCounter(4) <= P2KeyCounter(4) +1;
							ELSE
								P2KeyCounter(4) <= (OTHERS => '0');
							END IF;
							IF RawP2Key(5) = '1' THEN
								P2KeyCounter(5) <= P2KeyCounter(5) +1;
							ELSE
								P2KeyCounter(5) <= (OTHERS => '0');
							END IF;
							
						---------------------------------------------------------------------
						--END GENERATE;
						-- end of adding keyboard count
						GameCounterSlow <= GameCounterSlow + 1;
						
						PosX <= "00000";
						PosY <= "00001";
						
						IF P1KeyExit = '1' THEN
							state <= Pausing;
							Pause <= '1';
						ELSE
							state <= MapLooping;
							Pause <= '0';
						END IF;
					END IF;
				WHEN MapLooping =>					
					PrimWriteEnable <= '0';
					PrimWriteSelector <= '0';
					GameCounter <= GameCounter +1;
					IF PosX = 25 THEN
						IF PosY = 18 THEN
							state <= StoreMap;
							PosX <= "00000";
							PosY <= "00001";
		
						ELSE
							PosX <= "00001";
							PosY <= PosY +1;
							state <= UpdateMap_Load;
							DataLoadStart <= '1';
						END IF;
					ELSE
						PosX <= PosX +1;
						state <= UpdateMap_Load;
						DataLoadStart <= '1';
					END IF;
				WHEN UpdateMap_Load =>
					DataLoadStart <= '0';
					PrimWriteSelector <= '0';
					IF DataLoadFinish = '1' THEN
						state <= UpdateMap_Calculate;
						CalcCellStart <= '1';
					END IF;
				WHEN UpdateMap_Calculate =>
					CalcCellStart <= '0';
					IF CalcCellFinish = '1' THEN
						IF GameRes /= "00" THEN
							state <= GameOver;
						ELSE
							
							state <= UpdateMap_Store_Pre;		--store calced data to secoram
							SecoWriteEnable <= '0';
							SecoAddr <= PosY & PosX;
							SecoWriteData <= NCenter;
						END IF;
					END IF;
				WHEN UpdateMap_Store_Pre =>
					sCounter <= (OTHERS => '0');
					SecoWriteEnable <= '1';
					state <= UpdateMap_Store;
				WHEN UpdateMap_Store =>
					IF sCounter = 2 THEN
						SecoWriteEnable <= '0';
						state <= MapLooping;
					ELSE
						sCounter <= sCounter +1;
						state <= UpdateMap_Store;
					END IF;
				WHEN StoreMap =>
					PrimWriteEnable <= '0';
					PrimWriteSelector <= '1';			
					IF PosX = 25 THEN
						IF PosY = 18 THEN
							state <= GameLooping;
						ELSE
							PosX <= "00001";
							PosY <= PosY +1;
							state <= StoreMap_Read;
						END IF;
					ELSE									
						PosX <= PosX +1;
						state <= StoreMap_Read;
					END IF;
				WHEN StoreMap_Read =>
					SecoAddr <= PosY & PosX;
					sCounter <= (OTHERS => '0');
					state <= StoreMap_Read_Wait;
				WHEN StoreMap_Read_Wait =>
					IF sCounter = 1 THEN
						state <= StoreMap_Write;
						PrimWriteEnable <= '0';
					ELSE
						sCounter <= sCounter +1;
					END IF;
				WHEN StoreMap_Write =>
					PrimWriteAddr <= PosY & PosX;
					PrimWriteData <= SecoReadData;
					state <= StoreMap_Write_Pre;
				WHEN StoreMap_Write_Pre =>
					PrimWriteEnable <= '1';
					state <= StoreMap_Wait;
					sCounter <= (OTHERS => '0');
				WHEN StoreMap_Wait =>
					IF sCounter = 2 THEN
						state <= StoreMap;
						PrimWriteEnable <= '0';
					ELSE
						sCounter <= sCounter +1;
					END IF;
				WHEN GameOver =>
					Finish <= '1';
					state <= idle;
				WHEN Pausing =>
					IF RawP1Key(4) = '1' THEN
						state <= GameLooping;
						Pause <= '0';
					ELSE
						state <= Pausing;
						Pause <= '1';
					END IF;
				WHEN OTHERS =>
			END CASE;
		END IF;
	END PROCESS;
END rtl;
